// ======================================================================================
//  File:		UniversalEntityList.cs
//  Summary:	This sample provides an IList that can be bound directly to a 
//              standard .NET Data Grid.
// ======================================================================================
//
//  This file is part of the Microsoft CRM 4.0 SDK Code Samples.
//
//  Copyright (C) Microsoft Corporation.  All rights reserved.
//
//  This source code is intended only as a supplement to Microsoft
//  Development Tools and/or on-line documentation.  See these other
//  materials for detailed information regarding Microsoft code samples.
//
//  THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
//
// =======================================================================================

using System;
using System.Data;
using System.Collections;
using System.ComponentModel;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy.Metadata;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Metadata;
using System.Collections.Generic;

namespace Streamsol.Crm.XrmDevelopmentToolkit
{
    /// <summary>
    /// Provides an IList that can be bound directly to a standard .NET Data Grid
    /// </summary>
    public class UniversalEntityList : IList
    {
        private System.Collections.Generic.List<BusinessEntity> _entities;
        PropertyDescriptorCollection _columnDescriptors;

        /// <summary>
        /// Constructs a Universal Entity List. You may also use the BuildUniversalEntityList extension method on the MetaDataServive.
        /// </summary>
        /// <param name="crmEntities"></param>
        /// <param name="metaServiceUrl"></param>
        /// <param name="orgName"></param>
        public UniversalEntityList(BusinessEntityCollection crmEntities, string metaServiceUrl, string orgName)
        {
            // Standard MetaData Service Setup
            MetadataService metadataService = new MetadataService();
            metadataService.Credentials = System.Net.CredentialCache.DefaultCredentials;

            // Create an authentication token for your organization.
            CrmAuthenticationToken token = new CrmAuthenticationToken();
            token.OrganizationName = orgName;

            // Use Active Directory authentication.
            token.AuthenticationType = 0;

            metadataService.CrmAuthenticationTokenValue = token;
            metadataService.Url = metaServiceUrl;

            // Get the name of the entity
            // Note: We have to convert this to lower because RetrieveEntityMetadata expects the schema name
            string entityName = crmEntities.EntityName;
            entityName = entityName.ToLower();

            // Get the metadata for the currently list's entity
            // This metadata is used to create a "Property Descriptor Collection"
            RetrieveEntityRequest mdRequest = new RetrieveEntityRequest();
            mdRequest.EntityItems = EntityItems.EntityOnly | EntityItems.IncludeAttributes;
            mdRequest.LogicalName = entityName;
            mdRequest.RetrieveAsIfPublished = true;

            // Execute the request
            RetrieveEntityResponse entityResponse = (RetrieveEntityResponse)metadataService.Execute(mdRequest);

            EntityMetadata entityData = entityResponse.EntityMetadata;

            // Cache locals
            _columnDescriptors = UniversalEntity.CreatePropertyDescriptorCollection(entityData.Attributes);
            _entities = crmEntities.BusinessEntities;
        }

        public UniversalEntityList(PropertyDescriptorCollection columnDescriptors, List<BusinessEntity> entities)
        {
            _columnDescriptors = columnDescriptors;
            _entities = entities;
        }

        #region UniversalEntityListEnumerator Class

        /// <summary>
        /// Enumerator for the Universal Entity List
        /// </summary>
        class UniversalEntityListEnumerator : IEnumerator
        {
            private UniversalEntityList _entityList;
            private int _index = -1;

            internal UniversalEntityListEnumerator(UniversalEntityList entityList)
            {
                _entityList = entityList;
            }

            public bool MoveNext()
            {
                // If there are no records or moving forward would go past the total
                // number of available records, return false.
                if (_entityList._entities.Count == 0 || (_index + 1) >= _entityList._entities.Count)
                {
                    return false;
                }

                _index++;
                return true;
            }

            public void Reset()
            {
                _index = -1;
            }

            public virtual Object Current
            {
                get
                {
                    return _entityList[_index];
                }
            }
        }

        #endregion

        #region IList Members

        /// <summary>
        /// Read-only indexer that provides access to the list's Universal Entities
        /// </summary>
        public object this[int index]
        {
            get
            {
                DynamicEntity crmEntity = (DynamicEntity)_entities[index];

                UniversalEntity entity = UniversalEntity.CreateFromDynamicEntity(crmEntity, _columnDescriptors);

                return (object)entity;
            }
            set
            {
                throw new NotImplementedException("This method is not supported in this implementation");
            }
        }

        /// <summary>
        /// The total number of entities in the list
        /// </summary>
        public int Count
        {
            get { return _entities.Count; }
        }

        public bool IsFixedSize
        {
            get { return true; }
        }

        public bool IsReadOnly
        {
            get { return true; }
        }
        public virtual Object SyncRoot
        {
            get { return this; }
        }

        public virtual bool IsSynchronized
        {
            get { return false; }
        }

        public virtual IEnumerator GetEnumerator()
        {
            return new UniversalEntityListEnumerator(this);
        }

        #region Unsupported Features

        public void CopyTo(Array array, int index)
        {
            throw new NotImplementedException("This method is not supported in this implementation");
        }

        public int Add(object value)
        {
            throw new NotImplementedException("This method is not supported in this implementation");
        }

        public int IndexOf(object value)
        {
            throw new NotImplementedException("This method is not supported in this implementation");
        }

        public void Insert(int index, object value)
        {
            throw new NotImplementedException("This method is not supported in this implementation");
        }

        public void Remove(object value)
        {
            throw new NotImplementedException("This method is not supported in this implementation");
        }

        public void RemoveAt(int value)
        {
            throw new NotImplementedException("This method is not supported in this implementation");
        }

        public bool Contains(object value)
        {
            throw new NotImplementedException("This method is not supported in this implementation");
        }

        public void Clear()
        {
            throw new NotImplementedException("This method is not supported in this implementation");
        }

        #endregion

        #endregion

        #region ICollection Members

        void ICollection.CopyTo(Array array, int index)
        {
            foreach (var item in this._entities)
            {
                array.SetValue(item, index);
                index++;
            }
        }

        int ICollection.Count
        {
            get { return this._entities.Count; }
        }

        bool ICollection.IsSynchronized
        {
            get { return false; }
        }

        object ICollection.SyncRoot
        {
            get { return null; }
        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            return new UniversalEntityListEnumerator(this);
        }

        #endregion
    }
}
